#!/usr/bin/env python
from ants import *
from DataStructs import *
import DataStructs
# define a class with a do_turn method
# the Ants.run method will parse and update bot input
# it will also run the do_turn method for us
class MyBot:
    def __init__(self):
        # define class level variables, will be remembered between turn
       self.enemyList = [] # List of enemy ants 
       # self.attackRadius = ants.attackradius2
       
    # do_setup is run once at the start of the game
    # after the bot has received the game settings
    # the ants class is created and setup by the Ants.run method
    def do_setup(self, ants):
        # initialize data structures after learning the game settings
        self.hills = []
        self.visitedList = []
       # self.unseen = []
        self.undiscovered = []
       # self.undiscovered = aStar(ants.my_hills()[0]) # CHECK
      #  self.undiscovered = aStar((0,0))
       # for row in range(ants.rows):
       #     for col in range(ants.cols):
          #      self.unseen.append((row, col))
         
    # do turn is run once per turn
    # the ants class has the game state and is updated by the Ants.run method
    # it also has several helper methods to use
    def do_turn(self, ants):
        # track all moves, prevent collisions
        orders = {}
       # print ants.my_hills()[0]
        enemyList = ants.enemy_ants()

        
        def do_move_direction(loc, direction):
            new_loc = ants.destination(loc, direction)       
            for enemy in enemyList:
                   # if ants.visible(enemy[0]):
                        enemyDist = ants.distance(new_loc, enemy[0]) # Store distance between current location and enemy location
                        if enemyDist < 5 and ants.distance(loc, ants.my_hills()[0]) > 5 : # FIX DISTANCE Will avoid moving within attacking range of another ant
                          return False
            
            if (ants.unoccupied(new_loc) and new_loc not in orders): # checks if location is unoccupied and no ant has been assigned to move to that location
                ants.issue_order((loc, direction)) # Tells the ant to move in a given direction, loc is the current ant location 
                orders[new_loc] = loc # The orders list now includes an ant going to the new location 
                return True
            else:
                return False
        
        def do_move_direction_lite(loc, direction):
            new_loc = ants.destination(loc, direction) 
            if (ants.unoccupied(new_loc) and new_loc not in orders):       
                ants.issue_order((loc, direction)) # Tells the ant to move in a given direction, loc is the current ant location 
                orders[new_loc] = loc # The orders list now includes an ant going to the new location 
                return True
            else:
                return False

            
        targets = {} 
        
        def do_move_location(loc, dest):
            directions = ants.direction(loc, dest) # determine the 1 or 2 fastest (closest) directions to reach a location
            for direction in directions:
                if do_move_direction(loc, direction): # If possible to move there and another ant is not already ordered to move there
                    targets[dest] = loc # Key = successor location Value = current ant location
                    return True
            return False
        
        def do_move_location_lite(loc, dest):
            directions = ants.direction(loc, dest) # determine the 1 or 2 fastest (closest) directions to reach a location
            for direction in directions:
                if do_move_direction_lite(loc, direction): # If possible to move there and another ant is not already ordered to move there
                    targets[dest] = loc # Key = successor location Value = current ant location
                    return True
            return False
        
        def exploreMap():
            #undiscovered = aStar(ants.my_hills()[0]) # CHECK
            undiscovered = self.undiscovered
            for ant_loc in ants.my_ants():
                distList = []
                if ant_loc not in orders.values(): # Ant has not been assigned an order yet
                    for loc in undiscovered:
                        if ants.visible(loc):
                            self.undiscovered.remove(loc)
                        distList.append((ants.distance(ant_loc, loc), loc))
                    distList.sort()
                      #  if distance < minDistance:
                           # minDistance = distance
                          #  closestLoc = loc
                    for dist, loc in distList:
                        if do_move_location(ant_loc, loc): # Attempt to move to the closest undiscovered location
                            break 
        def aStar(ant_loc):
            visitedList = []
            undiscovered = []
            myPQ = DataStructs.PriorityQueue()
            myPQ.push(ant_loc, 0) # Store current ant_loc
            level = 0
            while not myPQ.isEmpty(): #level < 50:
               # level += 1
                currNode = myPQ.pop()
                if not ants.visible(currNode): # Check if current state is visible - this is a goal state
                    #undiscovered.append(ants.distance(ant_loc, currNode), currNode)
                    undiscovered.append(currNode)
                    return undiscovered
                if currNode not in visitedList:
                    visitedList.append(currNode)
                    for direction in ('s','e','w','n'):
                        myPQ.push (ants.destination(currNode, direction), 0)
          #  undiscovered.sort()
            return undiscovered 
        
        #if len(self.undiscovered) == 0:
        self.undiscovered = aStar(ants.my_hills()[0]) # Call A-Star to research map
        # prevent stepping on own hill
        for hill_loc in ants.my_hills():
            orders[hill_loc] = None # So no ants are ordered to step on the hill
            
        # Move Towards Closest Known Food
        ant_dist = [] # Holds the distance between an ant and food, as well as the ants current location and the food location
        for food_loc in ants.food(): # for every food location in the food list
            for ant_loc in ants.my_ants(): # for every ant we control
                dist = ants.distance(ant_loc, food_loc) # find distance between ant and food
                ant_dist.append((dist, ant_loc, food_loc))
        ant_dist.sort() # Put ants closest to food first in list
        for dist, ant_loc, food_loc in ant_dist:
            if food_loc not in targets and ant_loc not in targets.values(): # check to see if a food item already has an ant gathering it. 
                do_move_location(ant_loc, food_loc)
                
         
        # attack hills
        for hill_loc, hill_owner in ants.enemy_hills():
            if hill_loc not in self.hills: # Hill has yet to be discovered in previous turns
                self.hills.append(hill_loc)        
        ant_dist = []
        for hill_loc in self.hills:
            for ant_loc in ants.my_ants():
                if ant_loc not in orders.values(): # Current ant has not been assigned an order
                    dist = ants.distance(ant_loc, hill_loc) # Find distance between ant and enemy hill
                    ant_dist.append((dist, ant_loc))
        ant_dist.sort() # Closest ant to hill at beginning of list
        for dist, ant_loc in ant_dist: 
            do_move_location_lite(ant_loc, hill_loc) # Move towards enemy hill
        
        
       
                
        # Map Exploration (given)
        
                        
        # explore the map using aStar search 
        exploreMap()    
        """  
        for loc in self.unseen[:]: # Use list copy shortcut [:] to make sure the list we are looping through is different than the list we are removing locations from.
            if ants.visible(loc):
                self.unseen.remove(loc) # Location has been now discovered, take it out of unseen list
        for ant_loc in ants.my_ants():
            if ant_loc not in orders.values(): # Ant has not been assigned an order yet
                unseen_dist = []
                for unseen_loc in self.unseen:
                    dist = ants.distance(ant_loc, unseen_loc)
                    unseen_dist.append((dist, unseen_loc))
                unseen_dist.sort()
                for dist, unseen_loc in unseen_dist:
                    if do_move_location(ant_loc, unseen_loc): # Move towards an undiscovered area
                        break    
        """    
        # unblock own hill
        for hill_loc in ants.my_hills():
            if hill_loc in ants.my_ants() and hill_loc not in orders.values():
                for direction in ('s','e','w','n'):
                    if do_move_direction(hill_loc, direction):
                        break        
        
        """
        for ant_loc in ants.my_ants():
            if ant_loc not in orders.values():
                for direction in ('s','e','w','n'):
                    if do_move_direction(ant_loc, direction):
                        break                 
              #  do_move_direction(ant_loc, direction)     
       """                 
if __name__ == '__main__':
    # psyco will speed up python a little, but is not needed
    try:
        import psyco
        psyco.full()
    except ImportError:
        pass
    
    try:
        # if run is passed a class with a do_turn method, it will do the work
        # this is not needed, in which case you will need to write your own
        # parsing function and your own game state class
        Ants.run(MyBot())
    except KeyboardInterrupt:
        print('ctrl-c, leaving ...')
